//
//  AEControl.h
//  GameJam-Mac
//
//  Created by Clover Sin on 12/26/11.
//  Copyright (c) 2011 Snow Poem Society. All rights reserved.
//

#ifndef GameJam_Mac_AEControl_h
#define GameJam_Mac_AEControl_h

#include "stdafx.h"
#include "AETheme.h"
#include "AEUtility.h"

#include <list>

#define CLICK_DURATION 0.8

// Add a new mouse button state.
#define MOUSE_BUTTON_NONE (MOUSE_LEFT + MOUSE_RIGHT + MOUSE_MIDDLE)

enum AEControlState{
    CS_Normal = 0,
    CS_PressDown, 
    CS_MouseHover, 
};

/** 
 * Base class of all the view controls. 
 * AEControl is the unique legal element 
 *   of Editor's View. 
 * 
 * TODO: Use a quadtree instead of current simple one.
 */
class AEControl : public HUDActor
{
    friend class AEUIManager;
    
public:
    
    /**
     * Constructor, but should not be called directly, 
     *  use factory methods of AEUIManager instead. 
     * 
     * @param parent Parent control node
     * @param theme Current theme reference 
     */
    AEControl(AEControl* parent, AETheme* theme);
    
    virtual ~AEControl() {}
    
    /**
     * Check if it is visible.
     * 
     * @return Return true if it is visible 
     */
    bool isVisible();
    
    /**
     * Set it visible, both visible and enable.
     */
    void setVisible(bool flag);
    
    /**
     * Check if it is enable to respond. 
     * If control is disabled, it does not respond to anything. 
     * 
     * @return Return true if it is enabled 
     */
    bool isEnable();
    
    /**
     * Enable the control, and will respond.
     */
    void setEnable(bool flag);
    
    /** 
     * Check if it is currently waiting for modifying.
     */
    bool isActive();
    
    /** 
     * If it takes cursor away from parent, parent won't
     *   be able to handle mouse event now.
     */
    bool isOccupyingCursor() { return _occupyCursor; }
    void setOccupyCursor(bool flag) { _occupyCursor = flag; }
    
    /**
     * Use this method instead of Actor::SetPosition()
     *   since ui controls' position relate to their
     *   parents.
     */
    void setOffsetFromParent(const Vector2& offset);
    
    /** 
     * Offset from parent getter.
     */
    Vector2 getOffsetFromParent();
    
    // Set position of this control, should invoke this, 
    //   instead of Actor::SetPosition(). IMORTANT, 
    //   since this will sync children's position. 
    virtual void setControlPosition(const Vector2& new_pos);
    
    bool containsPointAt(const Vec2i& pos);
    bool containsPointAt(const Vector2& pos);
    bool containsPointAt(float x, float y);
    
    AEControl* getControlAt(const Vec2i& pos);
    AEControl* getControlAt(const Vector2& pos);
    AEControl* getControlAt(float x, float y);
    
    AEControl* getParentControl();
    
    /** 
     * Delegate property setter. 
     * 
     * @param delegate A delegate object which is 
     *   attaching to a C function or class method with 
     *   syntax like: void foo(AEControl* sender).
     *   To create a delegate object, use static method
     *   of AEDelegate: 
     *   - for C function: 
     *     AEDelegate::from_function<&foo>()
     *   - for normal member function(need instance obj): 
     *     AEDelegate::from_method<yourClass, &yourClass::m1>(&obj)
     *   - for const member function(Maybe useless, need instance obj): 
     *     AEDelegate::from_const_method<yourClass, &youClass::m2>(&obj)
     *   - for static member function: 
     *     AEDelegate::from_function<&yourClass::m3>()
     */
    void setDelegate(AEDelegate delegate);
    
    /** 
     * Update method which will be involked every frame. 
     * 
     * @param dt Delta time between 2 frames 
     */
    virtual void Update(float dt);
    
    virtual bool MouseMotion(Vector2 coor) { return false; }
    
    virtual bool MouseEnter(Vector2 coor) { return false; }
    
    virtual bool MouseLeave(Vector2 coor) { return false; }
    
    virtual bool MouseHover(Vector2 coor) { return false; }
    
    virtual bool MouseDown(Vector2 coor, MouseButtonInput button) { return false; }
    
    virtual bool MouseUp(Vector2 coor, MouseButtonInput button) { return false; }
    
    virtual bool MouseClick(MouseButtonInput button) { return false; }
    
    // This should and should ONLY be called 
    //   BEFORE adding it to the world. 
    virtual void prepareForAddingToWorld() {}
    
    /** 
     * Get collision bounding box of this control. 
     * 
     * @return Return the box structure 
     */
    virtual BoundingBox getCollisionBox();
    
protected:
    
    bool _visible;
    bool _enable;
    bool _active;
//    bool _ignored;
    bool _occupyCursor;
    
    // Use STL list to implement the control tree.
    std::list<AEControl*> _childControls;
    typedef std::list<AEControl*>::iterator ChildControlIt;
    typedef std::list<AEControl*>::reverse_iterator ChildControlRevIt;
    
    AEControl* _parentControl;
    
    AETheme* _theme;
    
    // Use layers to ensure child controls rendered before self.
    int _layerNumber;
    
    // Delegate is an object, returns false if it is "empty".
    AEDelegate _delegate;
    
    // Offset from parent, child's position relative to parent's.
    Vector2 _offsetFromParent;
    
    int getLayerNumber();
    
    // Update children's position related to their offset. 
    void syncChildrenPosition();
    
    // Current control state. 
    AEControlState _state;
    
    // Helper functions for mouse click checking. 
    bool _MouseDown(Vector2 coor, MouseButtonInput button);
    bool _MouseUp(Vector2 coor, MouseButtonInput button);
    void _Update(float dt);
    bool _MouseMotion(Vector2 coor);
    bool _MouseEnter(Vector2 coor);
    bool _MouseLeave(Vector2 coor);
    bool _MouseHover(Vector2 coor);
    
    float _timer;
    bool _countingTimer;
    int _mouseDownButton;
    
};

#endif
